19. Solution: Dependency Injection

Solution: Dependency Injection

ND079 JPND C2 L03 A19a Solution Dependency Injection V2

ExpirationChecker.java

import javax.inject.Inject;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;

public final class ExpirationChecker {

  private final Clock clock;
  private final MetadataFetcher metadataFetcher;

  @Inject
  ExpirationChecker(Clock clock, MetadataFetcher metadataFetcher) {
    this.clock = clock;
    this.metadataFetcher = metadataFetcher;
  }

  public List<Path> getExpiredFiles(List<Path> paths, Duration expiration) {
    return paths.stream()
        .filter((path) -> isExpired(path, expiration))
        .collect(Collectors.toList());
  }

  private boolean isExpired(Path path, Duration expiration) {
    Instant now = clock.instant();
    try {
      Instant modifiedTime = metadataFetcher.getLastModifiedTime(path);
      return now.isAfter(modifiedTime.plus(expiration));
    } catch (IOException e) {
      return false;
    }
  }
}

ExpirationCheckerModule.java

import com.google.inject.AbstractModule;

import java.time.Clock;

public final class ExpirationCheckerModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(MetadataFetcher.class).to(MetadataFetcherImpl.class);
    bind(Clock.class).toInstance(Clock.systemUTC());
  }
}

ExpirationCheckerTest.java

import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class ExpirationCheckerTest {
  public static void main(String[] args) {

    FakeClock fakeClock = new FakeClock();
    Map<Path, Instant> fakeModifiedTimes = new HashMap<>();
    FakeMetadataFetcher fakeMetadataFetcher = new FakeMetadataFetcher(fakeModifiedTimes);

    Injector injector = Guice.createInjector(
        b -> b.bind(Clock.class).toInstance(fakeClock),
        b -> b.bind(MetadataFetcher.class).toInstance(fakeMetadataFetcher));
    ExpirationChecker checker = injector.getInstance(ExpirationChecker.class);

    Path expired = Path.of("/test/expired");
    Path notExpired = Path.of("/test/not-expired");
    fakeModifiedTimes.put(expired, fakeClock.instant().minus(Duration.ofDays(31)));
    fakeModifiedTimes.put(notExpired, fakeClock.instant().minus(Duration.ofDays(27)));

    List<Path> expiredFiles =
        checker.getExpiredFiles(List.of(expired, notExpired), Duration.ofDays(30));

    assert expiredFiles.equals(List.of(expired));
  }
}

Main.java

import com.google.inject.Guice;
import com.google.inject.Injector;

import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public final class Main {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new ExpirationCheckerModule());
    ExpirationChecker checker = injector.getInstance(ExpirationChecker.class);

    List<Path> paths = Arrays.stream(args).map(Path::of).collect(Collectors.toList());

    System.out.println("The following files are expired: "
        + checker.getExpiredFiles(paths, Duration.ofDays(28)));
  }
}

So, Why Use Dependency Injection?

ND079 JPND C2 L03 A19b Why Use Dependency Injection